<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <p data-lake-id="udac36fe6" id="udac36fe6"><span data-lake-id="u3e0d1f0a" id="u3e0d1f0a">2020年5月份，Redis正式推出了6.0版本，这个版本中有很多重要的新特性，其中多线程特性引起了广泛关注。</span></p>
  <p data-lake-id="uc09491e5" id="uc09491e5"><br></p>
  <p data-lake-id="u03fbb544" id="u03fbb544"><span data-lake-id="u25deea36" id="u25deea36">但是，需要提醒大家的是，</span><strong><span data-lake-id="ua8cee4be" id="ua8cee4be">Redis 6.0中的多线程，也只是针对处理网络请求过程采用了多线程，而数据的读写命令，仍然是单线程处理的。</span></strong></p>
  <p data-lake-id="u23587efa" id="u23587efa"><br></p>
  <p data-lake-id="ucbaa9a05" id="ucbaa9a05"><span data-lake-id="uca97c654" id="uca97c654">但是，不知道会不会有人有这样的疑问：</span></p>
  <p data-lake-id="udc54649a" id="udc54649a"><br></p>
  <p data-lake-id="u620cf690" id="u620cf690"><strong><span data-lake-id="uf4dc7d25" id="uf4dc7d25">Redis不是号称单线程也有很高的性能么？</span></strong></p>
  <p data-lake-id="u4629f060" id="u4629f060"><br></p>
  <p data-lake-id="u9b98bdc4" id="u9b98bdc4"><strong><span data-lake-id="u271bba78" id="u271bba78">不是说多路复用技术已经大大的提升了IO利用率了么，为啥还需要多线程？</span></strong></p>
  <p data-lake-id="u37206857" id="u37206857"><br></p>
  <p data-lake-id="u8ab69f49" id="u8ab69f49"><span data-lake-id="u288408ed" id="u288408ed">主要是因为我们对Redis有着更高的要求。</span></p>
  <p data-lake-id="u2e0bd442" id="u2e0bd442"><br></p>
  <p data-lake-id="u1f30a921" id="u1f30a921"><span data-lake-id="u9b7687ff" id="u9b7687ff">根据测算，Redis 将所有数据放在内存中，内存的响应时长大约为 100 纳秒，对于小数据包，Redis 服务器可以处理 80,000 到 100,000 QPS，这么高的对于 80% 的公司来说，单线程的 Redis 已经足够使用了。</span></p>
  <p data-lake-id="u7251adcb" id="u7251adcb"><br></p>
  <p data-lake-id="u635fa94c" id="u635fa94c"><span data-lake-id="u056e0872" id="u056e0872">但随着越来越复杂的业务场景，有些公司动不动就上亿的交易量，因此需要更大的 QPS。</span></p>
  <p data-lake-id="u8ddb7039" id="u8ddb7039"><br></p>
  <p data-lake-id="u9a85c453" id="u9a85c453"><span data-lake-id="ubfa52842" id="ubfa52842">为了提升QPS，很多公司的做法是部署Redis集群，并且尽可能提升Redis机器数。但是这种做法的资源消耗是巨大的。</span></p>
  <p data-lake-id="uf2dee872" id="uf2dee872"><br></p>
  <p data-lake-id="udd4cd209" id="udd4cd209"><span data-lake-id="u9cf92b72" id="u9cf92b72">而经过分析，限制Redis的性能的主要瓶颈出现在网络IO的处理上，虽然之前采用了多路复用技术。但是我们前面也提到过，</span><strong><span data-lake-id="uf5ffae37" id="uf5ffae37">多路复用的IO模型本质上仍然是同步阻塞型IO模型</span></strong><span data-lake-id="u3fb66ba8" id="u3fb66ba8">。</span></p>
  <p data-lake-id="ud8e63748" id="ud8e63748"><br></p>
  <p data-lake-id="ude3db6a1" id="ude3db6a1"><span data-lake-id="u71627dea" id="u71627dea">下面是多路复用IO中select函数的处理过程：</span></p>
  <p data-lake-id="u2e2e424e" id="u2e2e424e"><br></p>
  <p data-lake-id="u9a1f677d" id="u9a1f677d"><img src="https://cdn.nlark.com/yuque/0/2022/jpeg/5378072/1671362670477-367e9055-e671-4ab8-abbe-46809592c4e9.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_41%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="u4bb17b8a" id="u4bb17b8a">​</span></p>
  <p data-lake-id="ud420950c" id="ud420950c"><br></p>
  <p data-lake-id="u67d441c9" id="u67d441c9"><span data-lake-id="u559b7817" id="u559b7817">从上图我们可以看到，</span><strong><span data-lake-id="u14ab6e96" id="u14ab6e96">在多路复用的IO模型中，在处理网络请求时，调用 select （其他函数同理）的过程是阻塞的，也就是说这个过程会阻塞线程，如果并发量很高，此处可能会成为瓶颈。</span></strong></p>
  <p data-lake-id="uef5c873e" id="uef5c873e"><br></p>
  <p data-lake-id="ud76cb254" id="ud76cb254"><span data-lake-id="u741ceb60" id="u741ceb60">虽然现在很多服务器都是多个CPU核的，但是对于Redis来说，因为使用了单线程，在一次数据操作的过程中，有大量的CPU时间片是耗费在了网络IO的同步处理上的，并没有充分的发挥出多核的优势。</span></p>
  <p data-lake-id="u8e9c16f9" id="u8e9c16f9"><br></p>
  <p data-lake-id="u334286b6" id="u334286b6"><strong><span data-lake-id="u67ad40d6" id="u67ad40d6">如果能采用多线程，使得网络处理的请求并发进行，就可以大大的提升性能。多线程除了可以减少由于网络 I/O 等待造成的影响，还可以充分利用 CPU 的多核优势。</span></strong></p>
  <p data-lake-id="u13644c8b" id="u13644c8b"><br></p>
  <p data-lake-id="u1e7e19a8" id="u1e7e19a8"><span data-lake-id="u0e1f674c" id="u0e1f674c">所以，Redis 6.0采用多个IO线程来处理网络请求，网络请求的解析可以由其他线程完成，然后把解析后的请求交由主线程进行实际的内存读写。提升网络请求处理的并行度，进而提升整体性能。</span></p>
  <p data-lake-id="ub73a7095" id="ub73a7095"><br></p>
  <p data-lake-id="uff1daf9f" id="uff1daf9f"><span data-lake-id="u06e3ca36" id="u06e3ca36">但是，Redis 的多 IO 线程只是用来处理网络请求的，对于读写命令，Redis 仍然使用单线程来处理。</span></p>
  <p data-lake-id="ue7d09dfe" id="ue7d09dfe"><br></p>
  <p data-lake-id="ue435a6fc" id="ue435a6fc"><strong><span data-lake-id="u851810e1" id="u851810e1">那么，在引入多线程之后，如何解决并发带来的线程安全问题呢？</span></strong></p>
  <p data-lake-id="ufde5919e" id="ufde5919e"><br></p>
  <p data-lake-id="u6ef58f99" id="u6ef58f99"><span data-lake-id="u47c6e239" id="u47c6e239">这就是为什么我们前面多次提到的"Redis 6.0的多线程只用来处理网络请求，而数据的读写还是单线程"的原因。</span></p>
  <p data-lake-id="uf53bc2a7" id="uf53bc2a7"><br></p>
  <p data-lake-id="uf22a3788" id="uf22a3788"><span data-lake-id="u449ab21b" id="u449ab21b">Redis 6.0 只有在网络请求的接收和解析，以及请求后的数据通过网络返回给时，使用了多线程。而数据读写操作还是由单线程来完成的，所以，这样就不会出现并发问题了。</span></p>
 </body>
</html>